a = 2;
var a;
console.log( a );
這應該會回傳 undefined 吧!
NO,是 2。
console.log( a );
var a = 2;
這樣呢?哼哼,這應該是 2 吧,照邏輯應該是這樣。
NO,是 undefined。
var a = 2;
其實是兩個述句組成。(先找出位置,再放進去。)var a;
和 a = 2;
宣告部分,會在編譯的過程處理。
指定部分,會留在原處等執行時處理。
所以第一個問題,重新改寫
var a; // 編譯先處理
a = 2; // 執行時處理
console.log( a ); // 2
第二個問題,重新改寫
var a;
console.log( a ); // undefined
a = 2;
這種把 var a;
提高的隱喻,大家叫他拉升 ( hoisting )。
###宣告 會先於 指定。
範例:
foo();
function foo(){
console.log( a );
var a = 2;
}
照邏輯改寫
function foo() {
var a;
console.log( a );
a = 2;
}
foo();
foo()
函式宣告和 var a
被拉升。
再執行 foo(),得到 undefined。
PS. 拉升要看範疇,所以 var a
不會拉到全域。
另一個例子。關於函式運算式 ( function expressions )
foo();
var foo = function bar(){
console.log("haha");
};
會變成。
var foo;
foo(); // Uncaught TypeError
foo = function bar() {
console.log("haha");
}
foo; // undefined
那關於 bar() 的部分呢?
bar(); // ReferenceError
在全域,他並不存在。
var foo = function bar(){
console.log("haha");
bar; // function bar();
}
但在內部他已經被宣告過了!
所以完整的改寫可以這樣做。
var foo;
foo(); // TypeError
bar(); // ReferenceError
foo = function() {
var bar = ...self...
console.log("haha");
}
目前為止,函數宣告和變數宣告都會被拉升。
但萬一! 函數命名和變數命名相同。
foo();
var foo;
function foo() { // 函數宣告
console.log( 1 );
}
foo = function(){ // 變數宣告
console.log( 2 );
}
這時候印出來的會是 1。
改寫後的邏輯是
function foo() { // hoisting
console.log( 1 );
}
foo(); // 1
foo = function(){
console.log( 2 );
}
注意 var foo;
是重複宣告,會忽略。因為函數宣告先於變數宣告。
但如果再次使用 foo(),則會印出 2。因為變數有了新的指定。
foo(); // 2
所以,雖然多重宣告會被忽略,但後續的函式宣告會覆寫之前的宣告。
foo(); // 3
function foo(){
console.log(1);
}
var foo = function bar(){ // 怎樣都輪不到他,函數宣告會大於變數宣告
console.log(2);
}
function foo(){ // 後面的會蓋住前面的
console.log(3);
}
最後一個範例,也是很神奇。
foo(); // "b"
if ( true ){
function foo(){ console.log("a"); }
}
else {
function foo(){ console.log("b"); }
}
這要表達的是,函式宣告會拉升到包含他的範疇 ( enclosing scope )
所以可以看作是
function foo(){ console.log("b"); }
foo();
if (true ){..}
else {..}